package com.osfw.activiti.service.running.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.osfw.activiti.domain.ProcessApproval;
import com.osfw.activiti.domain.ProcessExecResult;
import com.osfw.activiti.domain.ProcessNodeInfo;
import com.osfw.activiti.entity.history.ActHiExecinst;
import com.osfw.activiti.entity.history.ActHiProcinst;
import com.osfw.activiti.entity.history.ActHiTaskinst;
import com.osfw.activiti.entity.running.ActRuExec;
import com.osfw.activiti.enums.ProcApprovalResult;
import com.osfw.activiti.enums.ProcConcurrentPlan;
import com.osfw.activiti.enums.ProcExecResult;
import com.osfw.activiti.enums.SuspensionState;
import com.osfw.activiti.mapper.history.ActHiExecinstMapper;
import com.osfw.activiti.mapper.running.ActRuExecMapper;
import com.osfw.activiti.service.history.IActHiExecinstService;
import com.osfw.activiti.service.history.IActHiTaskinstService;
import com.osfw.activiti.service.resource.IActReProcdefService;
import com.osfw.activiti.service.running.IActRuExecService;
import com.osfw.framework.constants.Constants;
import com.osfw.framework.exception.ServiceException;
import com.osfw.framework.utils.SnowflakeUtil;
import com.osfw.framework.utils.bean.BeanMapper;
import com.osfw.system.entity.manage.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 流程节点信息表 服务层实现
 */
@Service
public class ActRuExecServiceImpl extends ServiceImpl<ActRuExecMapper, ActRuExec> implements IActRuExecService {

    @Resource
    private IActHiExecinstService hiExecinstService;
    @Resource
    private IActHiTaskinstService hiTaskinstService;
    @Autowired
    private IActReProcdefService procdefService;

    @Override
    public ProcessExecResult init(ActHiProcinst procinst, ProcessNodeInfo startNode, Date startTime, SysUser startUser, ProcessNodeInfo nextNode) {
        // 新增开始节点运行和历史流程执行实例
        Long startProExecId = SnowflakeUtil.generatorId();
        startNode.setAssignees(Arrays.asList(startUser));
        insertProExecinst(procinst.getProcDefId(), procinst.getProcInstId(), startProExecId, startProExecId, null, procinst.getBusiKey(), startNode);

        // 是否结束节点
        boolean isEndNode = procdefService.isEndNode(procinst.getProcDefId(), nextNode.getKey());
        if (isEndNode) {
            return new ProcessExecResult().setResult(ProcExecResult.PROCESS_ENDED.getResult());
        }
        // 新增下个节点运行和历史流程执行实例
        return insertProExecinst(procinst.getProcDefId(), procinst.getProcInstId(), null, startProExecId, startProExecId, procinst.getBusiKey(), nextNode);
    }

    @Override
    public ProcessExecResult agree(ProcessApproval approval, SysUser sysUser, Date currentTime) {
        ProcessExecResult execResult = null;
        // 查询当前流程执行信息，获取是否会签节点以及会签方案
        ActRuExec ruExec = getById(approval.getProcExecId());
        if (ruExec == null) {
            ServiceException.throw500("该任务已有人审批完成，请刷新页面！");
            return execResult;
        } else {
            // 判断当前节点是否会签节点
            if (ruExec.getIsConcurrent().intValue() == Constants.YES_INTEGER.intValue()) {
                // 会签节点处理逻辑：
                ProcConcurrentPlan concurrentPlan = ProcConcurrentPlan.getProcConcurrentPlan(ruExec.getConcurrentPlan());
                switch (concurrentPlan) {
                    case ONE_VOTE_PASSED: //一票通过
                        // 流转流程执行实例
                        execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                        break;
                    case ONE_VOTE_NO: //一票否决
                        /* 此处不存在该场景，无需处理 */
                        break;
                    case MULTIPLE_VOTE_PASSED: //多票通过，即同意票数过半视为通过
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 判断是否多票通过
                            if (isMultipleVotePassed(ruExec)) {
                                // 流转流程执行实例
                                execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                            } else {
                                // 终止流程执行实例
                                execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
                            }
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                    case NO_END_ALL_PASSED://一律通过（仅非结束节点），最终由下一节点或结束节点进行决策
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 流转流程执行实例
                            execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                }
            } else {
                // 流转流程执行实例
                execResult = circulateProExecinst(ruExec, sysUser, currentTime);
            }
        }
        return execResult;
    }

    @Override
    public ProcessExecResult goback(ProcessApproval approval, SysUser sysUser, Date currentTime) {
        ProcessExecResult execResult = null;
        // 查询当前流程执行信息，获取是否会签节点以及会签方案
        ActRuExec ruExec = getById(approval.getProcExecId());
        if (ruExec == null) {
            ServiceException.throw500("该任务已有人审批完成，请刷新页面！");
            return execResult;
        } else {
            // 判断当前节点是否会签节点
            if (ruExec.getIsConcurrent().intValue() == Constants.YES_INTEGER.intValue()) {
                ProcConcurrentPlan concurrentPlan = ProcConcurrentPlan.getProcConcurrentPlan(ruExec.getConcurrentPlan());
                switch (concurrentPlan) {
                    case ONE_VOTE_PASSED: //一票通过（回退），回退指定节点

                        break;
                    case ONE_VOTE_NO: //一票否决（回退），回退指定节点

                        break;
                    case MULTIPLE_VOTE_PASSED: //多票回退，即同意票数过半视为回退，回退指定节点
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 判断是否多票回退
                            if (isMultipleVotePassed(ruExec)) {
                                // 回退指定节点

                            } else {
                                // 终止流程执行实例
                                execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
                            }
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                    case NO_END_ALL_PASSED://一律通过（仅非结束节点），最终由下一节点或结束节点进行决策
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 流转流程执行实例
                            execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                }
            } else {
                // 终止流程执行实例
                execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
            }
        }
        return execResult;
    }

    @Override
    public ProcessExecResult disagree(ProcessApproval approval, SysUser sysUser, Date currentTime) {
        ProcessExecResult execResult = null;
        // 查询当前流程执行信息，获取是否会签节点以及会签方案
        ActRuExec ruExec = getById(approval.getProcExecId());
        if (ruExec == null) {
            ServiceException.throw500("该任务已有人审批完成，请刷新页面！");
            return execResult;
        } else {
            // 判断当前节点是否会签节点
            if (ruExec.getIsConcurrent().intValue() == Constants.YES_INTEGER.intValue()) {
                ProcConcurrentPlan concurrentPlan = ProcConcurrentPlan.getProcConcurrentPlan(ruExec.getConcurrentPlan());
                switch (concurrentPlan) {
                    case ONE_VOTE_PASSED: //一票通过
                        /* 此处不存在该场景，无需处理 */
                        break;
                    case ONE_VOTE_NO: //一票否决
                        // 终止流程执行实例
                        execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
                        break;
                    case MULTIPLE_VOTE_PASSED: //多票通过，即同意票数过半视为通过
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 判断是否多票通过
                            if (isMultipleVotePassed(ruExec)) {
                                // 流转流程执行实例
                                execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                            } else {
                                // 终止流程执行实例
                                execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
                            }
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                    case NO_END_ALL_PASSED://一律通过（仅非结束节点），最终由下一节点或结束节点进行决策
                        // 判断所有会签是否完成
                        if (isJoinAll(ruExec)) {
                            // 流转流程执行实例
                            execResult = circulateProExecinst(ruExec, sysUser, currentTime);
                        } else {
                            // 更新当前流程执行数据
                            execResult = updateProcExecinst(ruExec, sysUser, currentTime);
                        }
                        break;
                }
            } else {
                // 终止流程执行实例
                execResult = terminateProcExecinst(ruExec, sysUser, currentTime);
            }
        }
        return execResult;
    }


    /**
     * 判断会签所有签收人是否完成审批
     *
     * @param ruExec 流程执行实例
     */
    private boolean isJoinAll(ActRuExec ruExec) {
        LambdaQueryWrapper<ActRuExec> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ActRuExec::getProcExecParentId, ruExec.getProcExecParentId());
        queryWrapper.ne(ActRuExec::getProcExecId, ruExec.getProcExecId());
        queryWrapper.eq(ActRuExec::getIsConcurrent, Constants.YES_INTEGER);
        queryWrapper.eq(ActRuExec::getIsActive, Constants.YES_INTEGER);
        count(queryWrapper);
        return count(queryWrapper) == 0 ? true : false;
    }

    /**
     * 判断是否多票通过
     *
     * @param ruExec 流程执行实例
     */
    private boolean isMultipleVotePassed(ActRuExec ruExec) {
        LambdaQueryWrapper<ActRuExec> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(ActRuExec::getProcExecId);
        queryWrapper.eq(ActRuExec::getProcExecParentId, ruExec.getProcExecParentId());
        queryWrapper.ne(ActRuExec::getProcExecId, ruExec.getProcExecId());
        queryWrapper.eq(ActRuExec::getIsConcurrent, Constants.YES_INTEGER);
        queryWrapper.eq(ActRuExec::getIsActive, Constants.NO_INTEGER);
        List<ActRuExec> list = list(queryWrapper);
        List<Long> procExecIds = list.stream().map(x -> x.getProcExecId()).collect(Collectors.toList());
        List<ActHiTaskinst> hiTaskinstList = hiTaskinstService.selectHiTaskinstByExecIds(procExecIds);
        long passedCount = hiTaskinstList.stream().filter(x -> x.getResult().intValue() == ProcApprovalResult.AGREE.getResult().intValue()).count();
        long noPassedCount = hiTaskinstList.stream().filter(x -> x.getResult().intValue() == ProcApprovalResult.DISAGREE.getResult().intValue()).count() + 1l;
        return passedCount > noPassedCount ? true : false;
    }


    /**
     * 新增流程执行实例
     *
     * @param procDefId        流程定义ID
     * @param procInstId       流程实例ID
     * @param procExecRootId   流程执行根ID
     * @param procExecParentId 流程执行父级ID
     * @param busiKey          业务流程主键KEY
     * @param nextNode         下一个节点
     */
    private ProcessExecResult insertProExecinst(Long procDefId, Long procInstId, Long procExecId, Long procExecRootId, Long procExecParentId, String busiKey, ProcessNodeInfo nextNode) {
        ProcessExecResult execResult = null;
        if (nextNode.getAssignees() != null && !nextNode.getAssignees().isEmpty()) {
            Date currentTime = new Date();
            List<ActRuExec> ruExecList = new ArrayList<>();
            nextNode.getAssignees().stream().forEach(nextUser -> {
                ActRuExec ruNextExec = new ActRuExec()
                        .setProcExecId(procExecId)
                        .setProcExecRootId(procExecRootId)
                        .setProcExecParentId(procExecParentId)
                        .setProcDefId(procDefId)
                        .setProcInstId(procInstId)
                        .setBusiKey(busiKey)
                        .setActKey(nextNode.getKey())
                        .setActName(nextNode.getText())
                        .setActType(nextNode.getCategory())
                        .setStartTime(currentTime)
                        .setIsActive(Constants.YES_INTEGER)
                        .setIsConcurrent(nextNode.getIsConcurrent())
                        .setConcurrentPlan(nextNode.getConcurrentPlan())
                        .setSuspensionState(SuspensionState.RELEASE.getState());
                ruExecList.add(ruNextExec);
            });
            saveBatch(ruExecList);
            // 新增历史流程执行实例
            List<ActHiExecinst> hiExecinstList = BeanMapper.mapAsList(ruExecList, ActHiExecinst.class);
            hiExecinstService.saveBatch(hiExecinstList);

            execResult = new ProcessExecResult().setResult(ProcExecResult.PROCESS_EXECUTION.getResult()).setNextNodeExceinsts(ruExecList);
        } else {
            ServiceException.throw500("流程节点信息不全，未配置节点签收人！");
        }
        return execResult;
    }

    /**
     * 流转流程执行实例
     *
     * @param ruExec      执行实例
     * @param sysUser     操作用户
     * @param currentTime 操作时间
     */
    private ProcessExecResult circulateProExecinst(ActRuExec ruExec, SysUser sysUser, Date currentTime) {
        // 更新当前历史执行数据
        ActHiExecinst hiExecinst = hiExecinstService.getById(ruExec.getProcExecId());
        hiExecinst.setAssignee(sysUser.getUserId());
        hiExecinst.setEndTime(currentTime);
        hiExecinst.setDuration(currentTime.getTime() - hiExecinst.getStartTime().getTime());
        hiExecinstService.updateById(hiExecinst);
        // 删除当前所有执行数据
        LambdaQueryWrapper<ActRuExec> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ActRuExec::getProcExecParentId, ruExec.getProcExecParentId());
        remove(queryWrapper);

        // 获取下个节点
        ProcessNodeInfo nextNode = procdefService.getNextNodeInfo(ruExec.getProcDefId(), ruExec.getActKey());
        // 是否结束节点
        boolean isEndNode = procdefService.isEndNode(ruExec.getProcDefId(), nextNode.getKey());
        if (isEndNode) {
            return null;
        }
        return insertProExecinst(ruExec.getProcDefId(), ruExec.getProcInstId(), null, ruExec.getProcExecRootId(), ruExec.getProcExecId(), ruExec.getBusiKey(), nextNode);
    }

    /**
     * 更新当前流程执行实例
     *
     * @param ruExec      执行实例
     * @param sysUser     操作用户
     * @param currentTime 操作时间
     */
    private ProcessExecResult updateProcExecinst(ActRuExec ruExec, SysUser sysUser, Date currentTime) {
        // 更新当前历史执行数据
        ActHiExecinst hiExecinst = hiExecinstService.getById(ruExec.getProcExecId());
        hiExecinst.setAssignee(sysUser.getUserId());
        hiExecinst.setEndTime(currentTime);
        hiExecinst.setDuration(currentTime.getTime() - hiExecinst.getStartTime().getTime());
        hiExecinstService.updateById(hiExecinst);
        // 更新当前运行执行数据
        ruExec.setIsActive(Constants.NO_INTEGER);
        updateById(ruExec);

        return new ProcessExecResult().setResult(ProcExecResult.NODE_EXECUTION.getResult());
    }

    /**
     * 终止流程执行实例
     *
     * @param ruExec      执行实例
     * @param sysUser     操作用户
     * @param currentTime 操作时间
     */
    private ProcessExecResult terminateProcExecinst(ActRuExec ruExec, SysUser sysUser, Date currentTime) {
        // 更新当前历史执行数据
        ActHiExecinst hiExecinst = hiExecinstService.getById(ruExec.getProcExecId());
        hiExecinst.setAssignee(sysUser.getUserId());
        hiExecinst.setEndTime(currentTime);
        hiExecinst.setDuration(currentTime.getTime() - hiExecinst.getStartTime().getTime());
        hiExecinstService.updateById(hiExecinst);
        // 删除当前所有执行数据
        LambdaQueryWrapper<ActRuExec> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ActRuExec::getProcExecParentId, ruExec.getProcExecParentId());
        remove(queryWrapper);

        return new ProcessExecResult().setResult(ProcExecResult.PROCESS_ENDED.getResult());
    }

}
